#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_mixer.h"
#include "SDL/SDL_net.h"
#include "SDL/SDL_ttf.h"
#include <iostream>
#include <string>
#include <time.h>

#include "ConfigFile.h"
#include "Cursor/CursorEngine.h"
#include "Profiles.h"
#include "Timer.h"

#include "Interface.h"
#include "InterfaceMenu.h"
#include "InterfaceLobby.h"
#include "InterfaceOptions.h"
#include "InterfaceIngame.h"
#include "InterfaceGameSummary.h"

#include "StringUtil.h"
#include "Functions.h"
#include "Mapping/Tilesheets.h"
#include "SnakeEngine.h"
#include "FoodInfo.h"
#include "SummaryInfo.h"

#include <boost/lexical_cast.hpp>
using boost::lexical_cast;
using boost::bad_lexical_cast;

using namespace std;

// Function Prototypes
void quitGame();
int sdlSetup();
void ControlFPS();
void SwitchInterface(InterfaceType NewInterface);

// Screen surface and quit flag
SDL_Surface *screen = NULL;
bool bQuit = false;

// Holds the current game interface
Interface *currentInterface = NULL;

// Timer to control the framerate and the framerate limit variable
Timer tmrFramerate;
int iFPS;

int main(int argc, char* argv[])
{
    atexit(quitGame);

    srand(time(NULL));

    KeyBindings::Initialise();

    // Load the global config values from a config file
    ConfigFile::ReadFile("config.pcf");
    Profiles::ReadFile("data//profiles//profiles.txt");

    sdlSetup();

    CursorEngine::Initialise();
    CursorEngine::AddCursor("main", "data//cursors//main.png", Point(0, 0), "data//cursors//main.png");
    CursorEngine::SetActiveCursor("main");

    SDL_WM_SetCaption("SnakeWars - Pandepic 2009 - www.pandepic.net", "SnakeWars");

    tmrFramerate.Start();

    iFPS = 60;

    ConfigFile::strBGPath = "data//backgrounds//" + lexical_cast<string>(ConfigFile::iCurrentWindowResX) + "x" +
                                                    lexical_cast<string>(ConfigFile::iCurrentWindowResY) + ".png";

    currentInterface = new InterfaceMenu();
    currentInterface->Initialise();

    InterfaceType NewInterface = NONE;

    MapInfo::LoadMapData("data//maps//maplist.txt");
    FoodInfo::LoadFoodData("data//objects//food.txt");

    while (!bQuit)
    {
        try
        {
            // If the interface is null exit the game
            if (! currentInterface)
            {
                cout << "Closing Game: current interface is null." << endl;

                exit(1);
            }

            // Fill the screen to clear it
            SDL_FillRect(screen, NULL, 0xAF6D6D);

            // Object to store event data
            SDL_Event event;

            while (SDL_PollEvent(&event))
            {
                if (event.type == SDL_QUIT)
                {
                    exit(0);
                }

                NewInterface = currentInterface->HandleInput(event);

                CursorEngine::parseInput(event);

                if (NewInterface != NONE) { SwitchInterface(NewInterface); }
            }

            currentInterface->Draw(screen);

            CursorEngine::BlitToScreen(screen);

            // Swap buffers and render everything
            if (SDL_Flip(screen) != 0)
            {
                printf("SDL_Flip Error: %s\n", SDL_GetError());
            }

            ControlFPS();
        }
        catch(...)
        {
            cout << "Unknown exception caught" << endl;
        }
    }
}

void SwitchInterface(InterfaceType NewInterface)
{
    switch (NewInterface)
    {
        case EXIT:
        {
            exit(0);
        }
        break;

        case LOCALGAMELOBBY:
            delete currentInterface;
            currentInterface = new InterfaceLobby(LOCALGAMELOBBY);
            currentInterface->Initialise();
        break;

        case MENU:
            delete currentInterface;
            currentInterface = new InterfaceMenu();
            currentInterface->Initialise();
        break;

        case OPTIONS:
            delete currentInterface;
            currentInterface = new InterfaceOptions();
            currentInterface->Initialise();
        break;

        case INGAME:
            delete currentInterface;
            currentInterface = new InterfaceIngame();
            currentInterface->Initialise();
        break;

        case GAMESUMMARY:
            delete currentInterface;
            currentInterface = new InterfaceGameSummary();
            currentInterface->Initialise();
        break;
    }
}

void ControlFPS()
{
     while( tmrFramerate.getTicks() < (1000 / iFPS) ) // Regulate the FPS
     {
            // Make the process sleep until it reaches the max fps allowed
            SDL_Delay(int((1000 / iFPS) - tmrFramerate.getTicks()));
     }

     tmrFramerate.Start();
}

void quitGame()
{
    // Save Settings and Profiles
    ConfigFile::WriteFile("config.pcf");
    Profiles::WriteFile("data//profiles//profiles.txt");
    //********************************************

    KeyBindings::FreeKeys();
    MapInfo::FreeData();
    FoodInfo::FreeTypes();
    Tilesheets::Freesheets();
    SnakeEngine::Quit();
    SummaryInfo::FreeInfo();
    CursorEngine::FreeCursors();
    Profiles::ClearProfiles();

    delete currentInterface;

    Mix_CloseAudio();
    TTF_Quit();
    SDLNet_Quit();
    SDL_FreeSurface(screen);
    SDL_Quit();
}

int sdlSetup()
{
    // Initialize video and audio and return errors on fail
    if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0)
    {
        Uint32 init = SDL_WasInit(SDL_INIT_AUDIO);

        if(init & SDL_INIT_AUDIO)
        {
            // Audio init successful, use sound
        }
        else
        {
            // Audio init unsuccessful, don't use sound
            cout << "Unable to init Sound: " << SDL_GetError() << endl;
        }

        cout << "Unable to init SDL: " << SDL_GetError() << endl;

        return -1;
    }
    else
    {
        cout << "Video and Audio Initialised\n";
    }

    // Initialize the screen surface
    if (ConfigFile::bFullscreen == false)
    {
        screen = SDL_SetVideoMode(ConfigFile::iWindowResX, ConfigFile::iWindowResY, ConfigFile::iBPP,
                             SDL_HWSURFACE);
    }
    else
    {
        screen = SDL_SetVideoMode(ConfigFile::iWindowResX, ConfigFile::iWindowResY, ConfigFile::iBPP,
                             SDL_HWSURFACE | SDL_FULLSCREEN);
    }

    // if the screen wont initialize then return an error and exit game
    if ( screen == NULL )
    {
       cout << "Unable to set video: " << SDL_GetError() << endl;

       return -1;
    }

    if (!TTF_WasInit() && TTF_Init() == -1)
    {
        cout<< "TTF_Init: " << TTF_GetError() << endl;

        return -1;
    }

    // If audio wont initialize return an error
    if(Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 2048) < 0)
    {
        cout<< "Warning: Couldn't set audio: " << SDL_GetError() << endl;
    }

    if (SDLNet_Init() == -1)
    {
        cout << "SDLNet_Init: =" << SDLNet_GetError() << endl;

        return -1;
    }

    SDL_EnableUNICODE(1);
    SDL_EnableKeyRepeat(100, SDL_DEFAULT_REPEAT_INTERVAL);

    return 0;
}
